
import path from 'path';
import batch from './batch';
import scenes from './scenes';
import { KEY_SCREEN_CREATED, KEY_SCREEN_UPDATED, KEY_SCREEN_DESTROYED } from './events';
let ScreenStatus = batch.ScreenStatus;
let Scene = scenes.Scene;

/**
 * @param {string} scope
 * @return {Promise<ScreenStatus[]>} 从后台发来的展示屏幕状态数据列表，已经经过加工
 */
function fetchData(scope = null) {
  let url = (scope) ? '/aurora/monitor/status/scope/' + scope : '/aurora/monitor/status/all';

  return Promise.all([
    // 全部状态
    fetch(url)
      .then((res) => res.json())
      .then((_data) => {
        if (!_data.result) {
          throw _data;
        }

        /** @type {any[]} */
        let array = _data.data || [];
        return array.map(parseScreenStatus);
      })
      .then((array) => {
        batch.refreshAll(array);
        return array;
      }),
    // 全部场景静态数据
    fetch('/aurora/monitor/allSeries')
      .then((res) => res.json())
      .then((_data) => {
        if (!_data.result) {
          throw _data;
        }

        /** @type {any[]} */
        return parseScenes(_data.data);
      })
      .then((array) => {
        scenes.refreshAll(array);
        return array;
      }),

  ]).then(i2 => i2[0]); // 返回第一个, 即全部屏幕状态结果
}

/**
 * @returns {Promise<string[]>}
 */
function queryAllScopes() {
  return fetch('/aurora/monitor/scopes')
    .then(res => res.json())
    .then(o => o.result ? o.data.scopes : ['']);
}

/**
 * @param {string} seriesName 
 * @param {string} htmlPath 
 */
function fetchHtmlScript(seriesName, htmlPath) {
  return fetch(assetPath(seriesName, htmlPath))
    .then(res => res.text());
}

/**
 * @param {string} seriesName 
 * @param {string} childPath 
 */
function assetPath(seriesName, childPath) {
  return path.resolve('/aurora-res', seriesName, childPath);
}

/**
 * 用于为展示屏设置别名和 scope
 * GET: /aurora/monitor/alter/name/{sid}?name={name}
 * @param {string} sid 屏幕号
 * @param {string} alias 别名
 * @param {string} scope 所属空间
 * @returns {Promise<any>}
 */
function alterAlias(sid, alias, scope) {
  let obj = {};
  if (alias != null) {
    obj.name = alias;
  }
  if (scope != null) {
    obj.scope = scope;
  }

  return fetch(`/aurora/monitor/alter/screen/${sid}`, {
    method: 'post',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(obj),
  })
  .then((data) => data.json());
}

/**
 * 用于强制删除展示屏连接
 * GET: /aurora/monitor/alter/destroy/{sid}
 * @param {string} sid 屏幕号
 * @returns {Promise<any>}
 */
function destroyScreen(sid) {
  let url = `/aurora/monitor/alter/destroy/${sid}`;
  return fetch(url).then((data) => data.json());
}

/* **********
 * WebSocket *
 ********** */

/**
 * @type {WebSocket}
 */
let socket;
/**
 * @type {Function(string, any) : void}
 */
let msgListener;
/**
 * @type {Function() : void}
 */
let closeListener;

function connectWebSocket() {
  socket = new WebSocket('ws://' + location.host + '/ws/monitor');
  socket.onopen = function() {
    // Web Socket 已连接上，使用 send() 方法发送数据
    // this.send() | socket.send()
  };
  
  socket.onmessage = function (event) { 
    websocketListener(event);
  };
  
  socket.onclose = function() { 
    if (closeListener) {
      closeListener();
    }
  };
  
  socket.onerror = function(event) {
    console.error('socket.onerror', event);
  }
}

function websocketListener(event) {
  /*
event.data=字符串
{"result":true,"type":"screen-updated","data":
{"sid":"4ed1d7d1-1e0c-4460-80a0-3316277a4f62","name":"你好呀","resource":{"series":"timer","scene":"timer"},
"status":{"connect":true,"data":{"delta":0,"player":true,"pause":false},"instant":"2020-04-10 17:18:57"}}}
  */

  // 注意: fetcher 部分是不能发送订阅消息的, 所以需要添加 listener
  let data = JSON.parse(event.data);
  if (data.result !== false) {
    console.log('接收数据: ', data.type, data.data);

    switch (data.type) {
      case KEY_SCREEN_UPDATED: {
        let s = parseScreenStatus(data.data);
        batch.updateOne(s);
        if (msgListener) {
          msgListener.call(global, data.type, s);
        }
      } break;
      case KEY_SCREEN_CREATED: {
        let s = parseScreenStatus(data.data);
        batch.insertOne(s);
        if (msgListener) {
          msgListener.call(global, data.type, s);
        }
      } break;
      case KEY_SCREEN_DESTROYED: {
        let sid = data.data.sid;
        batch.deleteOne(sid);
        if (msgListener) {
          msgListener.call(global, data.type, sid);
        }
      } break;
    }
  }
}

function parseScreenStatus(item) {
  let s = new ScreenStatus();

  s.sid = item.sid;
  if (item.name) s.name = item.name;
  s.series = item.resource.series;
  s.scene = item.resource.scene;
  s.connect = item.status.connect;
  s.data = item.status.data;
  s.scope = item.scope;
  s.instant = new Date(item.status.instant);
  return s;
}

/**
 * @param {any} data
 * @returns {Scene[]}
 */
function parseScenes(data) {
  let scenes = [];

  // data.series
  if (data.series) {
    for (let seriesName in data.series) {
      let seriesChunk = data.series[seriesName];
      if (seriesChunk.scenes) {
        for (let i = 0; i < seriesChunk.scenes.length; i++) {
          let sceneChunk = seriesChunk.scenes[i];

          let s = new Scene();
          s.id = sceneChunk.id;
          s.name = sceneChunk.name;
          s.series = seriesName;
          s.jsEntry = sceneChunk['monitor-js'];
          s.htmlTemplate = sceneChunk['monitor-html'];
          scenes.push(s);
        }
      }
    }
  }

  return scenes;
}

/**
 * @param {Function(string, any) : void} l 可以为 null
 */
function setWebSocketMessageListener(l) {
  msgListener = l;
}

/**
 * @param {Function() : void} l 可以为 null
 */
function setWebSocketCloseListener(l) {
  closeListener = l;
}

/**
 * 用 Websocket 协议向服务器端发送屏幕指令
 * @param {string} sid 屏幕号
 * @param {string} command 命令
 * @param {any} arg 其它附加参数
 */
function sendCommand(sid, command, arg) {
  if ((!socket) || socket.readyState != socket.OPEN) {
    return; // 无法发送
  }

  socket.send(JSON.stringify({
    type : '/monitor/command',
    data : {
      id : sid,
      command : command,
      arg : arg
    }
  }));
}

/**
 * 用 Websocket 协议向服务器端发送切换空间命令
 * @param {string} scope 空间号
 */
function sendScopeUpdated(scope) {
  if ((!socket) || socket.readyState != socket.OPEN) {
    return; // 无法发送
  }

  socket.send(JSON.stringify({
    type : '/monitor/scope',
    data : { scope }
  }));
}

/* **********
 * 加载内容 *
 ********** */
/*
 * 动态加载 monitor-js 部分
 */

/**
 * @param {string} seriesName 
 * @param {string} sceneName
 * @returns {Promise<string[]>} [htmlText, jsText, manifestJson, js 加载位置]
 */
function fetchMonitorComponent(seriesName, sceneName) {
  const scene = scenes.getOne(seriesName, sceneName);
  if (!scene) {
    return Promise.reject({seriesName, sceneName});
  }

  const jsPath = scene.jsPath();
  const manifestPath = jsPath.substring(0, jsPath.length - 3) + "-manifest.json";

  return Promise.all([
    fetch(scene.htmlPath()).then(d => d.ok ? d.text() : Promise.reject(`${seriesName}/${sceneName} 的 html 文件不存在`)),
    fetch(jsPath).then(d => d.ok ? d.text() : Promise.reject(`${seriesName}/${sceneName} 的 js 文件不存在`)),
    fetch(manifestPath).then(d => {
      if (d.ok) {
        return d.json();
      }
      console.warn(`${seriesName}/${sceneName} 的 manifest 文件不存在`);
      return Promise.resolve(null);
    }),
    Promise.resolve(scene.jsEntry)
  ]);
}

export default {ScreenStatus, fetchData, queryAllScopes, fetchHtmlScript, assetPath, connectWebSocket,
  sendCommand, sendScopeUpdated, alterAlias, destroyScreen,
  setWebSocketMessageListener, setWebSocketCloseListener, fetchMonitorComponent};